package org.asmatron.messengine.engines.support;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import org.asmatron.messengine.MessEngine;
import org.asmatron.messengine.annotations.MessageMethod;
import org.asmatron.messengine.annotations.MessageListenerClass;
import org.asmatron.messengine.messaging.MessageListener;
import org.springframework.core.annotation.AnnotationUtils;

public class MessagingConfigurator {
	private MessEngine messEngine;

	public MessagingConfigurator() {
	}

	public MessagingConfigurator(MessEngine messEngine) {
		setMessEngine(messEngine);
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public int setupMessEngine(Object object) {
		int count = 0;
		MessageListenerClass selector = AnnotationUtils.findAnnotation(object.getClass(), MessageListenerClass.class);
		if (selector != null && object instanceof MessageListener) {
			MessageListener listener = (MessageListener) object;
			messEngine.addMessageListener(selector.value(), listener);
			count++;
		}
		List<Method> messageMethods = getMethods(object.getClass(), MessageMethod.class);
		if (messageMethods.size() == 0) {
			return count;
		}
		for (Method method : messageMethods) {
			addMessageMethodHandler(object, method);
		}
		return messageMethods.size() + count;
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public int resetMessEngine(Object object) {
		int count = 0;
		MessageListenerClass selector = AnnotationUtils.findAnnotation(object.getClass(), MessageListenerClass.class);
		if (selector != null && object instanceof MessageListener) {
			MessageListener listener = (MessageListener) object;
			messEngine.removeMessageListener(selector.value(), listener);
			count++;
		}
		List<Method> messageMethods = getMethods(object.getClass(), MessageMethod.class);
		if (messageMethods.size() == 0) {
			return count;
		}
		for (Method method : messageMethods) {
			removeMessageMethodHandler(object, method);
		}
		return messageMethods.size() + count;
	}

	private void addMessageMethodHandler(Object object, Method method) {
		checkMessEngine();
		MessageMethod annotation = method.getAnnotation(MessageMethod.class);
		MessageMethodListener listener = new MessageMethodListener(object, method);
		String id = annotation.value();
		messEngine.addMessageListener(id, listener);
	}

	private void removeMessageMethodHandler(Object object, Method method) {
		checkMessEngine();
		MessageMethod annotation = method.getAnnotation(MessageMethod.class);
		MessageMethodListener listener = new MessageMethodListener(object, method);
		String id = annotation.value();
		messEngine.removeMessageListener(id, listener);
	}

	private void checkMessEngine() {
		if (messEngine == null) {
			throw new IllegalStateException("Autoconfigure failed no messEngine set.");
		}
	}

	private List<Method> getMethods(Class<?> objectClass, Class<? extends Annotation> annotationClass) {
		List<Method> methods = new ArrayList<Method>();
		getMethods(objectClass, annotationClass, methods);
		return methods;
	}

	private void getMethods(Class<?> objectClass, Class<? extends Annotation> annotationClass, List<Method> methods) {
		if (objectClass != null) {
			Method[] declaredMethods = objectClass.getDeclaredMethods();
			for (Method method : declaredMethods) {
				if (method.isAnnotationPresent(annotationClass)) {
					methods.add(method);
				}
			}
			getMethods(objectClass.getSuperclass(), annotationClass, methods);
		}
	}

	public MessEngine getMessEngine() {
		return messEngine;
	}

	public void setMessEngine(MessEngine messEngine) {
		this.messEngine = messEngine;
	}

}
